﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Forms;
using System.Text;

namespace ShaderEffectCodeGenerator
{
    internal class Program
    {
        // Fields
        private static List<string> effectParameters = new List<string>();
        private static StreamWriter streamWriter;
        private static int tabLevel = 0;

        // Methods
        private static int LoadEffectFromFile(string fullPath, out Effect effect, out byte[] windowsByteCode, out byte[] xboxByteCode)
        {
            try
            {
                Form form = new Form();
                PresentationParameters parameters = new PresentationParameters();
                GraphicsDevice device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware, form.Handle, parameters);
                CompiledEffect effect2 = Effect.CompileEffectFromFile(fullPath, null, null, CompilerOptions.None, Microsoft.Xna.Framework.TargetPlatform.Windows);
                windowsByteCode = effect2.GetEffectCode();
                effect = new Effect(device, effect2.GetEffectCode(), CompilerOptions.None, null);
                CompiledEffect effect3 = Effect.CompileEffectFromFile(fullPath, null, null, CompilerOptions.None, Microsoft.Xna.Framework.TargetPlatform.Windows);//Xbox360);
                xboxByteCode = effect3.GetEffectCode();
            }
            catch (Exception exception)
            {
                ReportError(exception.ToString());
                effect = null;
                windowsByteCode = null;
                xboxByteCode = null;
                return -1;
            }
            return 0;
        }

        public static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                WriteInstructions();
                ReportError("Please drag an .fx file into the app.");
            }
            else
            {
                Effect effect;
                byte[] buffer;
                byte[] buffer2;
                string path = args[0];
                if (path.StartsWith("\"") && path.EndsWith("\""))
                {
                    path = path.Substring(0, path.Length - 1).Substring(1);
                }
                path = Path.GetFullPath(path);
                string str2 = Path.GetDirectoryName(path) + "/" + Path.GetFileNameWithoutExtension(path) + "Effect.cs";
                if (LoadEffectFromFile(path, out effect, out buffer, out buffer2) >= 0)
                {
                    if (File.Exists(str2))
                    {
                        File.Delete(str2);
                    }
                    string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
                    FileStream stream = new FileStream(str2, FileMode.Create);
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        streamWriter = writer;
                        WriteUsingStatements(writer);
                        WriteLine("namespace JEng_Mark_1.Shaders");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine(string.Format("public class {0}Effect : IJEngShader", fileNameWithoutExtension));
                        WriteLine("{");
                        tabLevel++;
                        WriteTechniqueEnumeration(effect);
                        streamWriter.WriteLine();
                        WriteRoeSpecificProperties(writer);
                        WriteLine("#region Effect Parameters");
                        writer.WriteLine();
                        foreach (EffectParameter parameter in effect.Parameters)
                        {
                            WriteEffectParameter(parameter);
                            writer.WriteLine();
                        }
                        WriteLine("#endregion");
                        writer.WriteLine();
                        WriteLine("#region Effect Techniques");
                        writer.WriteLine();
                        foreach (EffectTechnique technique in effect.Techniques)
                        {
                            WriteLine(string.Format("private EffectTechnique _{0}Technique;", technique.Name));
                            writer.WriteLine();
                        }
                        WriteLine("#endregion");
                        writer.WriteLine();
                        WriteLine("#region Initialize Methods");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Initializes the Effect from byte code for the given GraphicsDevice.");
                        WriteLine("///</summary");
                        WriteLine("///<param name=\"graphicsDevice\">The GraphicsDevice for which the effect is being created.</param>");
                        WriteLine("public void Initialize(GraphicsDevice graphicsDevice)");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("Initialize(graphicsDevice, CompilerOptions.None, null);");
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Initializes the Effect from byte code for the given GraphicsDevice and CompilerOptions.");
                        WriteLine("///</summary");
                        WriteLine("///<param name=\"graphicsDevice\">The GraphicsDevice for which the effect is being created.</param>");
                        WriteLine("///<param name=\"compilerOptions\">The CompilerOptions to use when creating the effect.</param>");
                        WriteLine("public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions)");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("Initialize(graphicsDevice, compilerOptions, null);");
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Initializes the Effect from byte code for the given GraphicsDevice, CompilerOptions, and EffectPool.");
                        WriteLine("///</summary");
                        WriteLine("///<param name=\"graphicsDevice\">The GraphicsDevice for which the effect is being created.</param>");
                        WriteLine("///<param name=\"compilerOptions\">The CompilerOptions to use when creating the effect.</param>");
                        WriteLine("///<param name=\"effectPools\">The EffectPool to use with the effect.</param>");
                        WriteLine("public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions, EffectPool effectPool)");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("_baseEffect = new Effect(graphicsDevice, byteCode, compilerOptions, effectPool);");
                        WriteLine("_readyToRender = true;");
                        writer.WriteLine();
                        foreach (string str4 in effectParameters)
                        {
                            WriteLine(string.Format("_{0}Param = _baseEffect.Parameters[\"{0}\"];", str4));
                        }
                        writer.WriteLine();
                        foreach (EffectTechnique technique2 in effect.Techniques)
                        {
                            WriteLine(string.Format("_{0}Technique = _baseEffect.Techniques[\"{0}\"];", technique2.Name));
                        }
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("#endregion");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Sets the current technique for the effect.");
                        WriteLine("///</summary>");
                        WriteLine("///<param name=\"technique\">The technique to use for the current technique.</param>");
                        WriteLine(string.Format("public void SetCurrentTechnique({0}Effect.Techniques technique)", fileNameWithoutExtension));
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("switch (technique)");
                        WriteLine("{");
                        tabLevel++;
                        foreach (EffectTechnique technique3 in effect.Techniques)
                        {
                            WriteLine(string.Format("case {0}Effect.Techniques.{1}:", fileNameWithoutExtension, technique3.Name));
                            tabLevel++;
                            WriteLine(string.Format("_baseEffect.CurrentTechnique = _{0}Technique;", technique3.Name));
                            WriteLine("break;");
                            writer.WriteLine();
                            tabLevel--;
                        }
                        tabLevel--;
                        WriteLine("}");
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("#region Compiled Byte Code");
                        writer.WriteLine();
                        WriteLine("#if XBOX");
                        StringBuilder builder = new StringBuilder();
                        builder.Append("static readonly byte[] byteCode = { ");
                        foreach (byte num in buffer2)
                        {
                            builder.Append(num.ToString() + ",");
                        }
                        builder.Append(" };");
                        WriteLine(builder.ToString());
                        WriteLine("#else");
                        builder = new StringBuilder();
                        builder.Append("static readonly byte[] byteCode = { ");
                        foreach (byte num2 in buffer)
                        {
                            builder.Append(num2.ToString() + ",");
                        }
                        builder.Append(" };");
                        WriteLine(builder.ToString());
                        WriteLine("#endif");
                        writer.WriteLine();
                        WriteLine("#endregion");
                        tabLevel--;
                        WriteLine("}");
                        tabLevel--;
                        WriteLine("}");
                    }
                }
            }
        }

        private static void WriteUsingStatements(StreamWriter writer)
        {
            WriteLine("using System;");
            WriteLine("using Microsoft.Xna.Framework;");
            WriteLine("using Microsoft.Xna.Framework.Content;");
            WriteLine("using Microsoft.Xna.Framework.Graphics;");
            WriteLine("using JEng_Mark_1.Interfaces;");
            writer.WriteLine();
        }

        private static void WriteRoeSpecificProperties(StreamWriter writer)
        {
            WriteLine("private Effect _baseEffect;");
            WriteLine("///<summary>");
            WriteLine("///Gets the underlying Effect.");
            WriteLine("///</summary>");
            WriteLine("public Effect BaseEffect");
            WriteLine("{");
            tabLevel++;
            WriteLine("get { return _baseEffect; }");
            tabLevel--;
            WriteLine("}");
            writer.WriteLine();
            WriteLine("private bool _readyToRender = false;");
            WriteLine("///<summary>");
            WriteLine("///Is the shader ready to be rendered.");
            WriteLine("///</summary>");
            WriteLine("public bool ReadyToRender");
            WriteLine("{");
            tabLevel++;
            WriteLine("get { return _readyToRender; }");
            tabLevel--;
            WriteLine("}");
            writer.WriteLine();
        }

        private static void WriteEffectParameter(EffectParameter param)
        {
            string item = param.Name;
            string str2 = String.Empty;
            string str3 = String.Empty;
            string str5 = String.Empty;
            switch (param.ParameterType)
            {
                case EffectParameterType.Bool:
                    str2 = "bool";
                    str3 = "Boolean";
                    break;
                case EffectParameterType.Int32:
                    str2 = "int";
                    str3 = "Int32";
                    break;
                case EffectParameterType.Single:
                    if ((param.RowCount > 1) && (param.ColumnCount > 1))
                    {
                        str2 = "Matrix";
                        str3 = "Matrix";
                    }
                    else if (param.ColumnCount > 1)
                    {
                        str2 = "Vector" + param.ColumnCount.ToString();
                        str3 = str2;
                    }
                    break;
                case EffectParameterType.String:
                    str2 = "string";
                    str3 = "String";
                    break;
                case EffectParameterType.Texture:
                    str2 = "Texture2D";
                    str3 = "Texture2D";
                    break;
                case EffectParameterType.Texture1D:
                    str2 = "Texture1D";
                    str3 = "Texture1D";
                    break;
                case EffectParameterType.Texture2D:
                    str3 = "Texture2D";
                    str3 = "Texture2D";
                    break;
                case EffectParameterType.Texture3D:
                    str2 = "Texture3D";
                    str3 = "Texture3D";
                    break;
                case EffectParameterType.TextureCube:
                    str2 = "TextureCube";
                    str3 = "TextureCube";
                    break;
                default:
                    return;
            }
            if (param.Elements.Count > 0)
            {
                str2 = str2 + "[]";
            }
            effectParameters.Add(item);
            string str4 = item.Substring(0, 1).ToUpper() + item.Substring(1);
            WriteLine(string.Format("private EffectParameter _{0}Param;", item));
            WriteLine(string.Format("public {0} {1}", str2, str4));
            WriteLine("{");
            tabLevel++;
            WriteLine("get");
            WriteLine("{");
            tabLevel++;
            WriteLine(string.Format("if (_{0}Param == null)", item));
            tabLevel++;
            WriteLine(string.Format("throw new Exception(\"Cannot get value of {0}; {0} EffectParameter is null.\");", str4));
            tabLevel--;
            if (param.Elements.Count > 0)
            {
                WriteLine(string.Format("return _{0}Param.GetValue{1}Array({2});", item, str3, param.Elements.Count));
            }
            else
            {
                WriteLine(string.Format("return _{0}Param.GetValue{1}();", item, str3));
            }
            tabLevel--;
            WriteLine("}");
            WriteLine("set");
            WriteLine("{");
            tabLevel++;
            WriteLine(string.Format("if (_{0}Param == null)", item));
            tabLevel++;
            WriteLine(string.Format("throw new Exception(\"Cannot set value of {0}; {0} EffectParameter is null.\");", str4));
            tabLevel--;
            WriteLine(string.Format("_{0}Param.SetValue(value);", item));
            tabLevel--;
            WriteLine("}");
            tabLevel--;
            WriteLine("}");
        }

        private static void WriteInstructions()
        {
            Console.WriteLine("The EffectCodeGenerator is a drop-in application. Simply drop in any .fx file to have");
            Console.WriteLine("the EffectCodeGenerator produce you a new class written in C# that enables easy use");
            Console.WriteLine("with your .fx file.");
        }

        private static void WriteLine(string line)
        {
            string str = string.Empty;
            for (int i = 0; i < tabLevel; i++)
            {
                str = str + "\t";
            }
            streamWriter.WriteLine(str + line);
        }

        private static void WriteTechniqueEnumeration(Effect effect)
        {
            WriteLine("public enum Techniques");
            WriteLine("{");
            tabLevel++;
            foreach (EffectTechnique technique in effect.Techniques)
            {
                WriteLine(string.Format("{0},", technique.Name));
            }
            tabLevel--;
            WriteLine("}");
        }

        private static void ReportError(string error)
        {
            Console.WriteLine("Error: {0}", error);
            Console.ReadKey(true);
        }
    }
}